;; dataobj0.lsp
;; Copyright (c) 1991-2000 by Forrest W. Young
;;


(defun load-data (&optional file (path *current-data-dir-name*) (keep-old-data-path? nil))
"Args: (&optional file (path *current-data-dir-name*) (keep-old-data-path? nil) )
Loads a data object contained in a file.  The file's name must end with .lsp.
If the optional string argument FILE is included, the data object is loaded 
from FILE, otherwise a dialog is presented to select the file. The string need not end with .lsp. The dialog opens at PATH, which defaults to the current data directory. The data directory that the data were obtained from is made the current data directory, unless keep-old-data-path? is t. Returns the object-id of the data object."
  (send *workmap* :redraw)
  (setf *show-info* t)
  (when (not file)
        (set-working-directory path)
        (setf file (read-file-dialog t))
        (unless keep-old-data-path? 
                (setf *current-data-dir-name* (pathname-device-directory file))))
  (send *workmap* :start-buffering)
  (reset-graphics-buffer)
  (send *workmap* :redraw)
  (when file
        (let ((object (send *workmap* :load-object file))
              (previous-previous-data previous-data))
          (when (not (objectp object))
                (error-message "File does not contain a legitimate data object.")
                ;(when *watcher* (send *watcher* :hide-window))
                (setf current-data previous-data)
                (setf previous-data previous-previous-data)
                (setf object nil))
          (send object :datafile file)
          object))
  (send *workmap* :redraw)
  (send *workmap* :buffer-to-screen)
  (reset-graphics-buffer)
  )


(defun pathname-device-directory (pname)
  (let ((device (PATHNAME-device pname))
        (directory (make-pathname :directory (PATHNAME-directory pname))))
    (strcat device ":"  directory)))

(defun data (&optional name &rest args) 
"The DATA function creates a new data object or reports the names of all data objects or the object identifcation of a specific object. It can be used in three different ways:
1) To see a list of all data objects, type:
   (DATA)
2) To see the object identification of data object NAME, type:
   (DATA NAME)
3) To create a new data object from information contained within the DATA statement, the minimum syntax requires you to type:
   (DATA 'NAME :VARIABLES <VARLIST> :DATA <DATALIST> )
GENERAL ARGUMENTS: 
&OPTIONAL NAME &KEY DATA VARIABLES TYPES LABELS FREQ ABOUT 
Defines ViSta data object NAME. NAME, DATA and VARIABLES are required arguments. 
  NAME must be a string or a symbol. If a symbol it must be preceeded by a single quote. The - (\"dash\") character cannot be used. It is reserved for special processing and is converted to _ (\"underscore\").
  
  DATA must be followed by a list of numbers, strings or symbols (or mix of such). Symbols are converted to uppercase strings. The number of data elements must conform to the information in other arguments.

  VARIABLES must be followed by a list of strings or a single quoted list of symbols defining variable names (and, indirectly, the number of variables). 

  TYPES, optional, must be a list of the strings \"numeric\", \"ordinal\" or \"category\" (case ignored), or a list of the corresponding symbols. These strings or symbols specify whether the variables are numeric, ordinal or categorical (all numeric by default). Note that the oridinal datatype is seldomly used.

  LABELS, optional, must be a list of strings or symbols specifying observation names (\"Obs1\", \"Obs2\", etc., by default). Symbols are converted to uppercase strings.

  FREQ specifies that the values of the numeric variables are frequencies. 

  ABOUT is an optional string of information about the data.

Given these arguments above you can specify the following types of data:
1) MULTIVARIATE data are data which are not one of the other data types given below. These data include univariate (one numeric or ordinal variable) and bivariate (two numeric or ordinal variables) data.
2) CATEGORY data have one or more CATEGORY variables and no NUMERIC or ORDINAL variables. The N category variables define an n-way classification.
3) CLASSIFICATION data have one NUMERIC variable and one or more CATEGORY variables. The N category variables define an n-way classification. The numeric variable specifies an observation for a given classification. 
4) FREQUENCY CLASSIFICATION data are classification data whose numeric variable specifies frequencies as indicated by using FREQ. The N category variables define an n-way classification, with the numeric variable specifying the co-occurance frequency of a specific combination of categories.

FREQUENCY TABLE DATA:
ARGUMENTS: &KEY ROW-LABEL, COLUMN-LABEL, and FREQ
For FREQUENCY TABLE data, the ROW-LABEL and COLUMN-LABEL arguments must be used: These data have NUMERIC variables whose values specify frequencies as indicated by using FREQ. The data are a two-way cross tabulation of the co-occurance frequency of the row and column entities. The ROW-LABEL and COLUMN-LABEL identify the data as two-way array (table) data, with the numeric variables in the data represent columns of a two-way table rather than variables of a multivariate dataset. ROW-LABEL and COLUMN-LABEL each have a string value which labels the rows or columns (ways) of the array. Observation labels are used to label the row-levels and variable names the column-levels. 

MATRIX DATA
ARGUMENTS: &KEY MATRICES SHAPES 
These arguments are used to identify matrix data. MATRICES, required for matrix data only, must be a list of strings specifying matrix names (and, indirectly, the number of matrices). SHAPES, optional for matrix data only, must be a list of strings \"symmetric\" or \"asymmetric\" (case ignored), specifying the shape of each matrix (all are symmetric by default). Matrix arguments cannot be used with array data.  

PROGRAMMING 
ARGUMENTS: &KEY PROGRAM, USE
If you wish to write a data program, use these arguments: 
1) PROGRAM (required) specifies that a program follows which computes N new variables. The program must return a list of N lists corresponding to the N variables in the :VARIABLES keyword. Causes the new variables to be bound.
2) USE (optional) specifies the data object whose variables are input to the program. This must be a data object with bound variables. If not, specify :USE (BIND-VARIABLES DOB) 

EFFICIENCY ARGUMENTS: &KEY MISSING-VALUES, STRINGS
1) If you know the data do or do not contain missing values, specifying MISSING-VALUES as T or NIL eliminates the time required to check for missing values.
2) Specify STRINGS T if you know that all category values are represented by strings (values inside double-quote marks) then the need to check for non-string category values is eliminated, greatly increasing efficiency, especially for large data.

SYSTEM ARGUMENTS (FOR USE BY SYSTEM DEVELOPERS, NOT BY USERS): 
&KEY DATATYPE CREATED CREATOR STARTER STARTUP SUBORDINATE ICONIFY DATASHEET-ARGUMENTS NEW-DATA ALL-TYPES-IN-DATA-ARRAY DATA-EDITOR
These arguments are internal system arguments. do not use."

  (let* ((i (position ':vdf args))
        ) 
    (when (and name (symbolp name))
          (setf name (string-downcase (format nil "~a" name))))
    (cond
      ((not name) (format t "~a~%" $data))
      ((not args)
       (cond
         ((objectp name) 
          (send name :info *standard-output* :verbose t)
          (format t ";         Variables: ~a" $vars))
         (t
          (error "not a data object"))))
      ((and (member ':vdf args) (1+ (position ':vdf args)))
       (setf args (adjoin name 
                          (select args 
                                  (remove i 
                                          (remove (1+ i) 
                                                  (iseq (length args)))))))
       (startup :startup nil :vdf args :verbose nil :infile *load-truename*))
      (t
       (if *load-verbose*
           (time (apply #'the-real-data-function (append (list name) args)))
           (apply #'the-real-data-function (append (list name) args)))))))


(defun the-real-data-function (name &key data (variables nil) (types nil) 
                  (array nil) (row-label nil) (column-label nil) (freq nil)
                  (matrices nil) (shapes nil) (strings nil) (labels nil)  
                  (program nil) (use nil) (missing-values nil) (title nil)
                  (created nil) (creator nil creator?) (subordinate nil) 
                  (iconify t) (about nil) (datatype nil) (known-as nil) (info t)
                  (all-types-in-data-array nil) (starter nil) (startup nil)
                  (datasheet-arguments nil) new-data (watcher t) ;;;; (data-editor nil)
                  (creator-object nil creator-object?) (new-datasheet nil))
;NAME
  (when (and name (symbolp name))
        (setf name (string-downcase (format nil "~a" name))))
  (setf name (replace-dash-with-underscore name))
  (when (and creator? creator-object?)
        (if (equal creator creator-object)
            (format t "~%; warning: you should not use both \"creator\" and its alias \"creator-object\".")
            (error "~%; inconsistent use of \"creator\" and its alias \"creator-object\".")))
  (when use (setcd use) (setf created (send *workmap* :selected-icon)))
  (let* ((dashicon)
         (setcd? t)
         (data-editor nil)
         (iconify (cond 
                    ((equal iconify "datasheet") 
                     (setf dashicon t) 
                     (setf setcd? nil)
                     (setf data-editor t)
                     t)
                    (iconify t)
                    (t nil)))
         (object)(ways)(element-labels)(classes)(previous-hilited-icon)
         (new-icon)(redraw-order (send *workmap* :redraw-order))
         (watcher-showing (if *watcher* (send *watcher* :showing) nil))
         (object-in current-object)(xy)(icon-type)(implied-icon-type)
         (datatype)(extension)(full-name)(expanded-data-type)(elipsis-name)(proper-name)
         (missing-values nil)(result))
;STARTER?  
    (when starter (setf starter (select (string-downcase starter) (iseq 5)))
          (when (or (equal starter "excel") (equal starter "acces"))
                (format t "; Loading DDE-Data[~a:~a] ~a~%"starter startup name)
               ; (startup-comment "Transfering Data")
                (setf result (convert-labeled-data-inline data variables types))
                (setf data (first result))
                (setf variables (second result))
                (setf types (third result))
                (setf labels (fourth result))
                (setf datavec (coerce data 'vector))
                (dotimes (i (length data))
                         (setf datum (select datavec i))
                         (when (and (stringp datum)
                                    (equal (string-downcase datum) "nil"))
                               (setf (select datavec i) NIL)))
                (setf data (coerce datavec 'list))
                ))
;NAME

    (when (and name (symbolp name))
          (setf name (string-downcase (format nil "~a" name))))
    (when (and known-as (symbolp known-as))
          (setf known-as (string-downcase (format nil "~a" known-as))))
    (when program (data-program-processor name program))
    (send *vista* :missing-values missing-values)
    (when (not *watcher*)
          (setf *watcher* (send *vista* :make-watcher :show watcher)))
    (when watcher
          (send *watcher* :write-text "Processing Datafile" :show watcher))
;NO PREVIOUS DATA
   ; (when (not previous-data) 
   ;       (send *workmap* :initialize-data-menu)
   ;       (mapcar #'(lambda (menu-item)
   ;                   (send menu-item :enabled t))
   ;               (send *desktop-window-menu* :items))
   ;       (send about-data-menu-item :enabled t)
   ;       (send *workmap* :initialize-file-menu current-data))
;VARIABLES
    (when (vectorp variables) (setf variables (coerce variables 'list)))
    (when (listp variables)
          (setf variables
                (mapcar #'(lambda (var)
                            (cond 
                              ((symbolp var) (setf var (string var)))
                              ((stringp var) var)
                              (t (error-message "Variable names must be symbols or strings"))))
                        variables)))
;TITLE
    (if (not title) (setf title name))

;TYPES
    (cond
     (types
      (when (vectorp types) (setf types (coerce types 'list)))
      (setf types
            (mapcar #'(lambda (type)
                        (cond 
                          ((symbolp type) (setf type  (string type)))
                          ((stringp type) type)
                          ((error-message "Variable types must be symbols or strings"))))
                    types)))
      (t (setf types (repeat "Numeric" (length variables)))))


;LABELS

    (when labels 
          (when (vectorp labels) (setf labels (coerce labels 'list)))
          (setf labels
                (mapcar #'(lambda (label)
                            (cond 
                              ((symbolp label) (setf label (string label)))
                              ((stringp label) label)
                              (error-message "Observation labels must be symbols or strings")))
                        labels)))


;NAME

    (setf name (get-sob-extension  name))
    (setf extension (third name))
    (setf name (second name))
    (setf expanded-data-type 
          (if data-editor "buf" (second (data-type types freq new-data nil matrices))))
    ;(setf proper-name (proper-name name expanded-data-type extension))
    (setf proper-name (concatenate-version name expanded-data-type))
    (setf full-name proper-name)
    (setf elipsis-name (elipsis-name full-name))

    (unless new-data
            (setf data (preprocess-data data variables types watcher strings)))
    (when data
          (send *workmap* :data-in-construction t) ;moved from above
          (when (not *watcher*)
                (setf *watcher* (send *vista* :make-watcher :show nil)))
          (when watcher
                (when *watcher* 
                      (send *watcher* :write-text "Creating Data Object." :show watcher)))
   ;icon-type 1=mv 2=method 3=model 4=matrix (5=table isnt used) 9=datasheet
          (cond
            (matrices
             (setf icon-type (if dashicon 9 4))
             (setf implied-icon-type 4)
             (setf object (matrix-data data variables title labels types 
                                       matrices shapes element-labels name 
                                       extension subordinate data-editor))
             (send object :data-type "matrix"))
            (t 
             (setf data-proto (if data-editor data-supervisor-proto mv-data-object-proto))
             (setf icon-type (if dashicon 9 1))
             (setf implied-icon-type 1)
             (setf object 
                   (send data-proto
                         :new data variables title labels types name 
                         freq nil row-label column-label array new-data extension
                         all-types-in-data-array subordinate)))))

    (setf known-as (if known-as known-as proper-name))
    (when known-as (send object :known-as known-as))

    (when object
          (send object :name name)
          (send object :extension extension)
          (send object :full-name full-name)
          (send object :proper-name proper-name)
          (send object :elipsis-name (elipsis-name (send object :proper-name)))
          (when watcher
              		(send *watcher* :write-text "Data Object Created." :show t))
          (send object :watcher watcher)
          (send object :freq-way-names (list row-label column-label))
          (if (not about) 
              (setf about (format nil "There is no information about this object."))) 
          (send object :about about)
          (send object :datasheet-arguments datasheet-arguments)
          (send object :iconify iconify)

;determine datatype

          (cond 
            ((or new-data (equal datatype "new"))
             (send object :new-data t)                  ;new statement. slot
             (setf datatype "new")                      ;and variable
             )
            ((or missing-values (send object :missing-data?))
             (send *vista* :missing-values t)
             (send object :missing-values t)
             (setf missing-values t)
             (setf datatype "missing")
             )
            (datatype
             )
            (t
             ;fwy moved following statement from inside "when iconify" below
             ;and added unless clause to improve efficiency
             (setf datatype (send object :determine-data-type))
             ))
          (send object :data-type datatype)
          (send object :real-data-type datatype) 
          (setf expanded-data-type datatype)


          (when iconify
               (send *vista* :data-object-list
                     (append (send *vista* :data-object-list) (list object))) 
               (send *watcher* :write-text "Updating WorkMap." :show t)
                (cond
                 ((not created)
                  (setf xy (send *workmap* :locate-new-icon))
                 (send *workmap* :add-icon ;*****  in next line
                        *workmap* (select xy 0) (select xy 1) 
                       (if known-as known-as 
                            (if (= extension 1) name full-name)) icon-type
                        expanded-data-type array :object object))
                 (created 
                  (when (not creator-object) (setf creator-object object-in))
                  (send object :add-parent creator-object)
                  (send creator-object :add-child object) ;*****  in next line
                  (send *workmap* :add-connected-icon created 
                        (if known-as known-as 
                            (if (= extension 1) name full-name)) icon-type
                        expanded-data-type :array array :object object
                        :implied-icon-type implied-icon-type)
                  ))


                (when (send *vista* :expert)
                    (setf created (send *expertmap* :selected-icon))) 
                (send object :icon-number (send *workmap* :num-icons))
                (send (select (send *workmap* :icon-list) 
                             (1- (send *workmap* :num-icons))) :object object)
                (when (and setcd? (send *vista* :long-menus))
                      (let ((item (send menu-item-proto :new full-name :mark t
                                        :action #'(lambda () (setcd object)))))
                        (send *data-menu* :append-items item)
                        (send item :add-slot 'object object)
                        (defmeth item :object (&optional (obj-id nil set)) 
                          (if set (setf (slot-value 'object) obj-id))
                          (slot-value 'object))
                        ))
                (when (not object-in) (setf object-in object))
                (when (not (send *vista* :long-menus))
                      (send object :menu-length 
                            (+ (send object :menu-length) 
                               (1- (length (send *workmap*
                                                 :data-icon-number-list))))))
                (cond
                 ((not (send *vista* :guidemap))
                  (when setcd? 
                        (setcd object nil :new-data new-data)
                        ))
                 ((send *vista* :guidemap)
                  (cond
                    ((or (equal (send object-in :slot-value 'proto-name)
                                (send mv-data-object-proto :slot-value
                                      'proto-name))
                         (equal (send object-in :slot-value 'proto-name)
                                (send table-data-object-proto :slot-value 
                                      'proto-name))
                         (equal (send object-in :slot-value 'proto-name)
                                (send diss-data-object-proto :slot-value 
                                      'proto-name)))
                     ;when object-in is data-object do setcd
                     (when setcd? (setcd object-in)))
                    (t (when investigate (format t 
             "DATA function assuming input object is model object.~%"))
                       (setcm object-in)))
                  (when created (send *guidemap* :created-data))))
                
                )
          (send *workmap* :data-in-construction nil); did have two parens at end?
          (setf new-icon (first (last (send *workmap* :icon-list))))
          (when previous-hilited-icon 
                (send previous-hilited-icon :show-icon "normal" :draw t))
          (when iconify (send new-icon :show-icon "selected" :draw t)))

    (when (send *workmap* :gui) 
          (send *workmap* :toolbar nil)
          (send *toolbar* :update-buttons)
          (send *workmap* :toolbar t)
          (send *workmap* :redraw-content))
    (when (send *vista* :obs-showing) (send *obs-window* :redraw))
    (when (send *vista* :vars-showing) (send *var-window* :redraw))
    ;(when recall-1click (setf *one-click-workmap* t))
    (when *watcher* (send *watcher* :close))
    (when (and iconify (not previous-data))
          (send *workmap* :toolbar t)
          (send *toolbar* :update-buttons)
          (send object :datasheet-object *desktop-datasheet*);fwy 102299
          (send delete-icon-menu-item :enabled t)
          )
    (when (not previous-data) 
          (send *workmap* :initialize-data-menu)
          (mapcar #'(lambda (menu-item)
                      (send menu-item :enabled t))
                  (send *desktop-window-menu* :items))
          (send about-data-menu-item :enabled t)
          (send *workmap* :initialize-file-menu current-data))
    (when (and *free-datasheets* (send object :datasheet-object))
          (send (send object :datasheet-object) :pop-out t))
    (when (send object :iconify) ($variables object))
    (when (and starter startup)
          (when *verbose* (format nil "DATA: STARTER STARTUP ~2a" starter startup))
          (msw-write-profile-string "ViSta" "ViStaOp" "yes" *ini-file*)
          (msw-write-profile-string "ViSta" "Starter" nil *ini-file*)
          (msw-write-profile-string "ViSta" "Startup" nil *ini-file*)
          (load (strcat *default-path* "startup\\" starter separator startup))
          )
	
    (send object :datasheet nil)
    (send object :elapsed-time
          (/ (- (get-internal-real-time) (send object :statobj-start-time))
             internal-time-units-per-second))
    (send object :instance-info (format nil "~a" (select (date-time) 9)))
    (send object :icon-title full-name);name?
    (when (and (send object :iconify)
               (not (equal "buffer" (send object :name)))
               (not (equal "hidden" (send object :name)))
               info 
               *show-info*)
          (send object :info);SLOWSLOWSLOWSLOWSLOWSLOWSLOWSLOW
          )
                                                        
    object))

(defun data-program-processor (name program)
  "Args: NAME PROGRAM
NAME is a string which is the name of the data-object being created. PROGRAM is the result of a Lisp form. If the result consists of a list of equal-length lists, these lists are converted into :DATA for data object NAME. Otherwise there must be one or more equal-length $NAME-VARS (created by using the VAR function in the body of PROGRAM). The result of each $NAME-VAR is used to form the list of equal-length lists which are then converted into :DATA for data object NAME. "

  (let* ((data)
         )
    (cond
      ((list-of-equal-length-lists-p data)
       (combine (row-list (transpose (apply #'bind-rows program)))))
      (t
       (error-message "Data program does not return a list of equal-length lists")
       nil))))



(defun list-of-equal-length-lists-p (arg)
  (and (listp arg)
            (every #'listp arg)
            (= 1 (length (unique-values (map-elements 
                          #'length arg))))))

(defun convert-labeled-data-inline (data-list vars types)
  (let* ((in-nvar (length vars))
         (ndatums (length data-list))
         (in-nobs (/ ndatums in-nvar))
         (quit (unless (integerp in-nobs) 
                       (error-message "Data are Non-Rectangular")
                       (top-level)))
         (in-data-mat (matrix (list in-nobs in-nvar) data-list))
         (in-data-row-list (row-list in-data-mat))
         (out-vars (rest (coerce (select in-data-row-list 0) 'list )))
         (out-nvar (length out-vars))
         (out-nobs (- in-nobs 2))
         (out-types (rest (coerce (select in-data-row-list 1) 'list )))
         (out-data-mat (select in-data-mat (iseq 2 (- in-nobs 1)) (iseq 1 (- in-nvar 1))))
         (out-labs (select (coerce (select (column-list in-data-mat) 0) 'list)
                           (iseq 2 (1+ out-nobs))))
         (out-data-list)
         )
    (dotimes (i out-nobs) 
             (dotimes (j out-nvar)
                      (unless (equal (string-downcase (select out-types j)) "category")
                              (setf (aref out-data-mat i j) 
                                    (number-from-string (aref out-data-mat i j))))))
    (setf out-data-list (combine (row-list out-data-mat)))
    (list out-data-list out-vars out-types out-labs)))

(defun table-data (data variables title labels types name ways classes)
  (load (strcat *vista-dir-name* "tablobj1"))
  (load (strcat *vista-dir-name* "tablobj2"))
  (table-data data variables title labels types name ways classes))

(defun matrix-data (data variables title labels types matrices shapes
                         element-labels name extension)
  (load (strcat *vista-dir-name* "dissobj"))
  (matrix-data 
   data variables title labels types matrices shapes element-labels name extension))

(defun preprocess-data (data variables types watcher strings)
  (let* ((sizes (check-rectangularity data variables))
                (nvar (length variables))
         (nobs) (nvar) (datamat) (type-logic) (ncats))
    (when sizes
          (setf nobs (first sizes))
          (setf nvar (second sizes))
          (setf datamat (matrix sizes data))
          (setf type-logic 
           (mapcar #'(lambda (type) 
                       (equal "category" (string-downcase type))) types))
          (when (not type-logic) (setf type-logic (repeat nil nvar)))
          (setf data (clean-data datamat nobs nvar ncats type-logic watcher variables))
          (cond
            ((second data)
             (error-message (format nil "Numeric variable(s) contain non-numeric information (string or symbol values). See BAD DATA report for details." ))
             (setf data nil)
             (send *workmap* :data-in-construction nil)
             (when (send *workmap* :gui t)(send *workmap* :redraw))
             (top-level))
            (t
             (setf data (first data))))
          )
    (if sizes data nil)))

(defun check-rectangularity (data variables)
  (let* ((nvar (length variables))
         (nobs (/ (length data) nvar)))
    (cond
      ((/= 0 (rem (length data) nvar))
       (error-message "Data are not rectangular. The number of data values must be an integer multiple of the number of variables.")
       nil)
      (t
       (list nobs nvar)))))

(defun clean-data (datamat nobs nvar ncats type-logic watcher variables)
  (let ((numvar-list nil)
        (datum))
    (when watcher (send *watcher* :show-window))
    (dotimes (j nvar)
             (when watcher 
                   (send *watcher* :write-text 
                         (format nil "Cleaning Variables~%~D of ~D cleaned." j nvar)))
             (cond
               ((select type-logic j) 
                (dotimes (i nobs)
                         (convert-catval datamat i j)))
               (t
                (dotimes (i nobs)
                         (setf datum (aref datamat i j))
                         (unless (or (numberp datum) 
                                    ; (stringp (eval datum))
                                    ; (equal (string-downcase (eval datum)) "nil")
                                     )

                                 (cond
                                  ; ((equal (eval datum) "nil")
                                  ;  (setf datum 'nil)
                                  ;  (setf (select datamat i j) 'nil))
                                   ((null datum)
                                    (send *vista* :missing-values t)
)
                                   ((missingp-fwy datum)
                                    (send *vista* :missing-values t)
                                    )
                                   (t
                                    (when (not numvar-list)
                                          (setf w (report-header "Bad Data Report" :page t))
                                          (display-string (format nil "The following numeric variables have non-numeric values.~%") w))
                                    (display-string 
                                     (format nil "~%Variable ~a, Observation ~d: Datavalue = ~a"
                                             (select variables j) i datum) w)
                                    
                                    (setf numvar-list (append numvar-list (list j)))
                                    (return))))))))
    (when watcher 
          (send *watcher* :write-text (format nil "Numeric variables are clean")))
    (list (combine (row-list datamat)) numvar-list)))

(defun convert-catval (datamat i j)
  (let ((datum (aref datamat i j)))
    (if (symbolp datum) 
        (if (null datum)
            (send *vista* :missing-values t)
            (setf (aref datamat i j) (string datum)))
        (when (numberp datum) (setf (aref datamat i j) (format nil "~S" datum))))))


(setf *current-data* nil)
